﻿using MiniFox.Data.Mapping;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Linq;
using System.Linq.Expressions;
using System.Text;

namespace MiniFox.Data.NativeClient
{

    /// <summary>
    /// 实体单表查询对象
    /// </summary>
    /// <typeparam name="TEntity"></typeparam>
    public abstract class NativeDataQuery<TEntity> : NativeDataCommand<TEntity>, IDataQuery<TEntity> where TEntity : class
    {
        /// <summary>
        /// 
        /// </summary>
        string _baseSql;
        /// <summary>
        /// 
        /// </summary>
        ParameterCollection _parameters;
        /// <summary>
        /// 
        /// </summary>
        /// <param name="database"></param>
        public NativeDataQuery(Database database) : base(database)
        {
        }
        /// <summary>
        /// 
        /// </summary>
        protected virtual string BaseSqlText
        {
            get
            {
                if (string.IsNullOrWhiteSpace(_baseSql))
                {
                    _baseSql = DatabaseUtil.ResolveBaseSqlExpression<TEntity>(this.DbSchema);
                }
                return this._baseSql;
            }
        }
        /// <summary>
        /// 
        /// </summary>
        protected virtual ParameterCollection Parameters
        {
            get
            {
                if (_parameters == null)
                    _parameters = new ParameterCollection();
                return _parameters;
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="lambda"></param>
        /// <returns></returns>
        protected virtual string ResolveExpression(Expression<Func<TEntity, bool>> lambda)
        {
            if (lambda == null)
                return this.BaseSqlText;

            this.Parameters.Clear();
            Expression exp = lambda.Body;
            ParameterExpression pExp = lambda.Parameters[0];
            if (typeof(TEntity) != pExp.Type)
                throw new InvalidOperationException();
            string mainSql = $"{this.BaseSqlText} WHERE {DatabaseUtil.ResolveConditionExpression(exp, pExp, this.Parameters, this.Database.ParameterPrefix)}";

            return mainSql;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="lambda"></param>
        /// <param name="sortFields"></param>
        /// <returns></returns>
        public virtual ResultSet<TEntity> Execute(Expression<Func<TEntity, bool>> lambda, IDictionary<string, bool> sortFields)
        {
            string sqlstr = $"{ResolveExpression(lambda)} {DatabaseUtil.ResolveSortExpression(sortFields)}";
            Dictionary<string, object> paramDict = new Dictionary<string, object>();
            ResultSet<TEntity> result = new ResultSet<TEntity>();
            try
            {
                List<TEntity> list = new List<TEntity>();
                List<DbParameter> parameterlist = new List<DbParameter>();
                foreach (var p in this.Parameters)
                {
                    DbParameter par = this.Database.CreateParameter(p.ParameterName, p.Name, p.ClrType, p.Direction);
                    par.Value = p.DefaultValue;
                    parameterlist.Add(par);
                    paramDict.Add(par.ParameterName, par.Value);
                }
                this.Database.ExecuteTimeout = this.TimeOut;
                this.OnCreated(sqlstr, paramDict);
                int rowCount = this.Database.ExecuteReader(list, sqlstr, parameterlist.ToArray());
                result.RowCount = rowCount;
                result.Data = list;

                this.OnExecuted(sqlstr, paramDict);
            }
            catch (Exception e)
            {
                this.OnErrorHandled(sqlstr, paramDict, e);
            }
            return result;
        }

        /// <summary>
        /// 分页查询，并统计总行数
        /// </summary>
        /// <param name="lambda"></param>
        /// <param name="sortFields"></param>
        /// <param name="pageIndex"></param>
        /// <param name="pageSize"></param>
        /// <returns></returns>
        public virtual PagedResultSet<TEntity> Execute(Expression<Func<TEntity, bool>> lambda, IDictionary<string, bool> sortFields, int pageIndex, int pageSize)
        {
            pageIndex = (pageIndex <= 0) ? 1 : pageIndex;
            pageSize = (pageSize <= 0) ? 20 : pageSize;

            string mainSql = ResolveExpression(lambda);
            string sqlstr = CreateCommandTextForPagedQuery(mainSql, sortFields, pageIndex, pageSize);
            Dictionary<string, object> paramDict = new Dictionary<string, object>();
            PagedResultSet<TEntity> result = new PagedResultSet<TEntity>(pageIndex, pageSize);

            try
            {
                List<TEntity> list = new List<TEntity>();
                List<DbParameter> parameterlist1 = new List<DbParameter>();
                List<DbParameter> parameterlist2 = new List<DbParameter>();
                foreach (var p in this.Parameters)
                {
                    DbParameter par;
                    par = this.Database.CreateParameter(p.ParameterName, p.Name, p.ClrType, p.Direction);
                    par.Value = p.DefaultValue;
                    parameterlist1.Add(par);
                    par = this.Database.CreateParameter(p.ParameterName, p.Name, p.ClrType, p.Direction);
                    par.Value = p.DefaultValue;
                    parameterlist2.Add(par);
                    paramDict.Add(par.ParameterName, par.Value);
                }
                this.Database.ExecuteTimeout = this.TimeOut;

                this.OnCreated(sqlstr, paramDict);
                int effectCount = this.Database.ExecuteReader(list, sqlstr, parameterlist1.ToArray());
                int rowCount = this.Database.ExecuteScaler<int>(CreateRowCountCommandText(mainSql), parameterlist2.ToArray());
                result.RowCount = rowCount;
                result.Data = list;

                this.OnExecuted(sqlstr, paramDict);
            }
            catch (Exception e)
            {
                this.OnErrorHandled(sqlstr, paramDict, e);
            }
            return result;
        }
        /// <summary>
        /// 返回top 查询结果，并统计总行数
        /// </summary>
        /// <param name="lambda"></param>
        /// <param name="sortFields"></param>
        /// <param name="maxCount"></param>
        /// <returns></returns>
        public virtual ResultSet<TEntity> Execute(Expression<Func<TEntity, bool>> lambda, IDictionary<string, bool> sortFields, int? maxCount)
        {
            int cnt = maxCount ?? 10;
            string mainSql = ResolveExpression(lambda);
            string sqlstr = CreateCommandTextForLimitedQuery(mainSql, sortFields, cnt);
            Dictionary<string, object> paramDict = new Dictionary<string, object>();
            ResultSet<TEntity> result = new ResultSet<TEntity>();

            try
            {
                List<TEntity> list = new List<TEntity>();
                List<DbParameter> parameterlist1 = new List<DbParameter>();
                List<DbParameter> parameterlist2 = new List<DbParameter>();
                foreach (var p in this.Parameters)
                {
                    DbParameter par;
                    par = this.Database.CreateParameter(p.ParameterName, p.Name, p.ClrType, p.Direction);
                    par.Value = p.DefaultValue;
                    parameterlist1.Add(par);
                    par = this.Database.CreateParameter(p.ParameterName, p.Name, p.ClrType, p.Direction);
                    par.Value = p.DefaultValue;
                    parameterlist2.Add(par);
                    paramDict.Add(par.ParameterName, par.Value);
                }
                this.Database.ExecuteTimeout = this.TimeOut;
                this.OnCreated(sqlstr, paramDict);
                int effectCount = this.Database.ExecuteReader(list, sqlstr, parameterlist1.ToArray());
                int rowCount = this.Database.ExecuteScaler<int>(CreateRowCountCommandText(mainSql), parameterlist2.ToArray());
                result.RowCount = rowCount;
                result.Data = list;

                this.OnExecuted(sqlstr, paramDict);
            }
            catch (Exception e)
            {
                this.OnErrorHandled(sqlstr, paramDict, e);
            }
            return result;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="mainSql"></param>
        /// <returns></returns>
        protected virtual string CreateRowCountCommandText(string mainSql)
        {
            string sql = $"SELECT COUNT(1) FROM({mainSql}) T";
            return sql;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="mainSql"></param>
        /// <param name="sortFields"></param>
        /// <param name="pageIndex"></param>
        /// <param name="pageSize"></param>
        /// <returns></returns>
        protected abstract string CreateCommandTextForPagedQuery(string mainSql, IDictionary<string, bool> sortFields, int pageIndex, int pageSize);
        /// <summary>
        /// 
        /// </summary>
        /// <param name="mainSql"></param>
        /// <param name="sortFields"></param>
        /// <param name="maxCount"></param>
        /// <returns></returns>
        protected abstract string CreateCommandTextForLimitedQuery(string mainSql, IDictionary<string, bool> sortFields, int maxCount);

        /// <summary>
        /// 
        /// </summary>
        /// <param name="queryable"></param>
        /// <param name="expression"></param>
        /// <param name="sortFields"></param>
        /// <param name="maxRowCount"></param>
        /// <returns></returns>
        public abstract ResultSet<TEntity> Execute(IQueryable<TEntity> queryable, Expression<Func<TEntity, bool>> expression, IDictionary<string, bool> sortFields, int? maxRowCount);

        /// <summary>
        /// 
        /// </summary>
        /// <param name="queryable"></param>
        /// <param name="expression"></param>
        /// <param name="sortFields"></param>
        /// <returns></returns>
        public abstract ResultSet<TEntity> Execute(IQueryable<TEntity> queryable, Expression<Func<TEntity, bool>> expression, IDictionary<string, bool> sortFields);

        /// <summary>
        /// 
        /// </summary>
        /// <param name="queryable"></param>
        /// <param name="expression"></param>
        /// <param name="sortFields"></param>
        /// <param name="pageIndex"></param>
        /// <param name="pageSize"></param>
        /// <returns></returns>
        public abstract PagedResultSet<TEntity> Execute(IQueryable<TEntity> queryable, Expression<Func<TEntity, bool>> expression, IDictionary<string, bool> sortFields, int pageIndex, int pageSize);
        
    }

}
